/*
 * testScheduler.c
 *
 * Created: 2015-01-05 11:29:52
 *  Author: Andreas Domfors
 */ 

#include <stdbool.h>
#include <string.h>
#include <malloc.h>
#include "drivers/usart.h"
#include "API/rosa.h"
#include "ee_printf.h"
#include "kernel/rosa_queues.h"
#include "kernel/rosa_scheduler.h"
#include "kernel/rosa_util.h"

#include "testFramework.h"

static void task1(void)
{
	taskResults[0] = 1;
	while(1) {
	}
}

static void testSimpleTaskCreation(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("test", task1, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(taskResults[0], 1);
}

static void RelativeDelayTestTaskOne(void)
{
	while(1)
	{
		timerType after, before = ROSA_getCurrentTick();
		ROSA_taskDelay(10);
		after = ROSA_getCurrentTick();
		taskResults[0] = after - before;
	}
}

static void RelativeDelayTestTaskTwo(void)
{
	while(1)
	{
		timerType after, before = ROSA_getCurrentTick();
		ROSA_taskDelay(10);
		after = ROSA_getCurrentTick();
		taskResults[1] = after - before;
	}
}

static void testRelativeDelays(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("tst1", RelativeDelayTestTaskOne, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("tst2", RelativeDelayTestTaskTwo, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(taskResults[0], 10);
	testIntEq(taskResults[1], 10);
}

static void AbsoluteDelayTestTaskOne(void)
{
	while(1)
	{
		timerType after, before = ROSA_getCurrentTick();
		ROSA_taskDelayUntil(before, 10);
		after = ROSA_getCurrentTick();
		taskResults[0] = after - before;
	}
}

static void AbsoluteDelayTestTaskTwo(void)
{
	while(1)
	{
		timerType after, before = ROSA_getCurrentTick();
		ROSA_taskDelayUntil(before, 10);
		after = ROSA_getCurrentTick();
		taskResults[1] = after - before;
	}	
}

static void testAbsoluteDelays(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("tst1", AbsoluteDelayTestTaskOne, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("tst2", AbsoluteDelayTestTaskTwo, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(taskResults[0],10);
	testIntEq(taskResults[1],10);
}

static void testSameNameTaskCreation(void)
{
	int result;
	//Create tasks and install them into the ROSA kernel
	result = ROSA_taskCreate("tst1", AbsoluteDelayTestTaskOne, 1, DEFAULT_STACK_SIZE);
	testIntEq(result, 0);
	result = ROSA_taskCreate("tst1", AbsoluteDelayTestTaskTwo, 1, DEFAULT_STACK_SIZE);	
	testIntEq(result, -1);
}

static void LowPrioTask(void)
{
	while(1) {
		taskResults[0] = 1;
	}
}

static void HighPrioTask(void)
{
	while(1);
}

static void testStaticTaskPriority(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("tst1", LowPrioTask, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("tst2", HighPrioTask, 5, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(taskResults[0], 0);
}

static void dynamicTaskCreator(void)
{
	int result;
	result = ROSA_taskCreate("tst1", RelativeDelayTestTaskOne, 1, DEFAULT_STACK_SIZE);
	testIntEq(result, 0);
	ROSA_taskDelay(20);
	ROSA_taskCreate("tst2", RelativeDelayTestTaskTwo, 1, DEFAULT_STACK_SIZE);
	testIntEq(result, 0);
	ROSA_taskDelay(20);
	
	while(1) {
		taskResults[2] = 1;
	}
}

void testDynamicTaskCreation(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("tst0", dynamicTaskCreator, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(taskResults[0], 10);
	testIntEq(taskResults[1], 10);
	testIntEq(taskResults[2], 1);
}

static void testOutOfMemoryCondition(void)
{
	int counter = 0;
	int result;
	char name[] = "tst0";
	
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	while(1) {
		name[3] = '0' + counter++;
		result = ROSA_taskCreate(name, task1, 1, DEFAULT_STACK_SIZE);
		switch(result)
		{
			case 0:
				continue;
			case -1:
				ee_printf("Name collision: %.4s\n", name);
				return;
			case -2:
				ee_printf("Out of memory. Created %d tasks\n", counter);
				goto runScheduler;
		}
	}
	runScheduler:
	{
		struct mallinfo m = mallinfo();
		ee_printf("Malloc memory used: %d free: %d bytes total: %d bytes\n", m.uordblks, m.fordblks, m.arena);	
		ROSA_startScheduler();
	}
}

static void suicidalTask(void)
{
	ROSA_taskDelay(10);
	ROSA_taskTerminate();
}

static void testTaskDeletion(void)
{
	//Create tasks and install them into the ROSA kernel
	ROSA_taskCreate("tst1", suicidalTask, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("tst2", suicidalTask, 1, DEFAULT_STACK_SIZE);
	ROSA_taskCreate("stop", schedulerStopper, 10, DEFAULT_STACK_SIZE);
	
	//Start the ROSA kernel
	ROSA_startScheduler();
	
	testIntEq(readyQueueLength(), 2);
	testIntEq(deleteQueueLength(), 0);
}

static TestEntry entries[] = {
	ENTRY(testSimpleTaskCreation),
	ENTRY(testRelativeDelays),
	ENTRY(testAbsoluteDelays),
	ENTRY(testSameNameTaskCreation),
	ENTRY(testStaticTaskPriority),
	ENTRY(testDynamicTaskCreation),
	ENTRY(testOutOfMemoryCondition),
	ENTRY(testTaskDeletion),
};

void testScheduler(void)
{
	ee_printf("TESTING SCHEDULER\n");
	performTests(entries, length(entries));
	ee_printf("Scheduler test complete\n");
}